import boss from "./assets/boss.png"

export class LolGame {
  constructor(props) {
    if (!props) throw new Error('请传入canvas参数')
    // 2d实例
    this.ctx = props.ctx
    // canvas实例
    this.canvas = props.canvas
    // canvas宽度
    this.canvasW = this.canvas.width
    // canvas高度
    this.canvasH = this.canvas.height
    // 创建怪兽定时器
    this.createBossInterval = null
    // 怪兽列表
    this.bossTaskQueue = []
    // 全局右键点击事件
    this.eventContextmenu = this.eventContextmenu.bind(this)
    // 英雄配置
    this.heroConfig = {
      heroNameW: null,
      runQueue: [],
      speed: 5,
      heroName: '无名氏', // 英雄名称
    }
    // 全局配置
    this.config = {
      animationID: null,
      gameGrade: 1, // 游戏难度
    }
    // 开始
    this.init()
  }

  /**
   * @description 重置画布
   */
  resetCanvas () {
    this.ctx.clearRect(0, 0, this.canvasW, this.canvasH)
  }
  /**
   * @description 初始化英雄名称
   * @param name
   */
  initHeroName (name) {
    if (name) this.heroConfig.heroName = name
  }
  /**
   * @description 绘制英雄名称在屏幕中心, 并记录位置, 后续会根据位置移动
   */
  drawHeroName () {
    const { ctx, canvasW, canvasH, heroConfig: { heroName } } = this
    ctx.font = "18px sans-serif"
    // 当heroNameW不存在的时候，需要初始化位置
    if (!this.heroConfig.heroNameW) {
      // 获取文本宽度，根据上面的18px
      this.heroConfig.heroNameW = ctx.measureText(heroName).width
      this.heroConfig.currentX = canvasW / 2 - this.heroConfig.heroNameW / 2
      this.heroConfig.currentY = canvasH / 2 - 6
    }

    const find = this.bossTaskQueue.find(boss => {
      const BOSSLx = boss.currentX
      const BOSSRx = boss.currentX + boss.width
      const BOSSTy = boss.currentY
      const BOSSBy = boss.currentY + boss.height
      return this.isIntersection(
          this.heroConfig.currentX,
          this.heroConfig.currentX + this.heroConfig.heroNameW,
          this.heroConfig.currentY,
          this.heroConfig.currentY + 18,
          BOSSLx, BOSSRx, BOSSTy, BOSSBy
      )
    })
    if (find) {
      alert('结束了别玩了')
    }
    ctx.fillStyle = 'pink'
    ctx.fillText(heroName, this.heroConfig.currentX , this.heroConfig.currentY)
  }
  /**
   * @description 移动英雄
   */
  moveHero () {
    const { heroConfig: { currentX, currentY, speed, runQueue } } = this
    if (runQueue.length) {
      const { x, y } = this.moveFn(currentX, currentY, runQueue[0].endX, runQueue[0].endY, speed)
      // 如果x坐标返回异常就清除移动任务
      if (x === undefined) {
        this.heroConfig.runQueue.pop()
        return
      }
      this.heroConfig.currentX += x
      this.heroConfig.currentY += y
    }
    this.drawHeroName()
  }
  /**
   * @description 计算点与点之间的每次x, y轴需要走的步数
   * @param currentX 当前X
   * @param currentY 当月Y
   * @param endX 终点X
   * @param endY 终点Y
   * @param speed 步数
   * @return {{}|{x: number, y: number}}
   */
  moveFn (currentX, currentY, endX, endY, speed) {
    // 根据当前位置，以及终点位置，计算路线
    const dx = endX - currentX
    const dy = endY - currentY
    const distance = Math.sqrt(dx * dx + dy * dy)
    if (distance > speed) {
      return {
        x: (dx / distance) * speed,
        y: (dy / distance) * speed
      }
    }
    return {}
  }
  /**
   * @description 右键点击事件
   * @param e
   */
  eventContextmenu (e) {
    // 阻止默认的上下文菜单弹出
    e.preventDefault()
    this.heroConfig.runQueue = [{ endX: e.clientX, endY: e.clientY }]
  }
  /**
   * @description 判断是否交集
   * @param leftA A左边距左距离
   * @param rightA A右边距左距离
   * @param topA A上边距上距离
   * @param bottomA A下边距上距离
   * @param leftB B的
   * @param rightB B的
   * @param topB B的
   * @param bottomB B的
   * @return {boolean}
   */
  isIntersection (leftA, rightA, topA, bottomA, leftB, rightB, topB, bottomB) {
    // leftA > rightB  物体A的左X轴位置，比物体B的右X位置还大，证明A 完全在B的右面, 反之，完全在左面
    // topA > bottomB  物体A的上Y轴位置，比物体B的下Y位置还大，证明A 完全在B的下面, 反之，完全在上面
    // 只要满足以下四个条件中的一个，即可断定没有任何交集
    return !(leftA > rightB || rightA < leftB || topA > bottomB || bottomA < topB);
  }
  /**
   * 创建怪兽插入到怪兽列表
   */
  createBoss () {
    this.createBossInterval = setInterval(() => {
      const bossConfig = new RoleFactory({
        width: 52,
        height: 60,
        imageSrc: boss
      }).roleConfig
      bossConfig.currentX = Math.random() * this.canvasW
      bossConfig.currentY = 0.5 > Math.random() ? Math.random() * 100 : (Math.random() * (this.canvasH  - (this.canvasH - 100) + 1)) + (this.canvasH - 100)
      let dx = this.heroConfig.currentX - bossConfig.currentX
      let dy = this.heroConfig.currentY - bossConfig.currentY
      let length = Math.sqrt(dx * dx + dy * dy)
      let unitDx = dx / length
      let unitDy = dy / length
      let farX = this.heroConfig.currentX + unitDx * this.canvasH / 2
      let farY = this.heroConfig.currentY + unitDy * this.canvasH / 2
      bossConfig.endX = farX
      bossConfig.endY = farY
      bossConfig.speed = this.config.gameGrade * 2
      bossConfig.image.onload = () => {
        this.bossTaskQueue.push(bossConfig)
      }
    }, 1000)
  }
  drawBoss () {
    const { ctx } = this
    this.bossTaskQueue = this.bossTaskQueue.filter(i => !i.endFlag)
    this.bossTaskQueue.forEach(bossItem => {
      ctx.save()
      if (new Date().getTime() > bossItem.updateTime + 100) {
        bossItem.index++
      }
      ctx.drawImage(bossItem.image, bossItem.index * 64, 0, 60, 60, bossItem.currentX, bossItem.currentY, bossItem.width, bossItem.height)
      if (new Date().getTime() > bossItem.updateTime + 100) {
        if (bossItem.index >= 31) {
          bossItem.index = 0
        }
        bossItem.updateTime = new Date().getTime()
      }
      const { x, y } = this.moveFn(bossItem.currentX, bossItem.currentY, bossItem.endX, bossItem.endY, bossItem.speed)
      if (x === undefined) {
        bossItem.endFlag = true
      } else {
        bossItem.currentX += x
        bossItem.currentY += y
      }
      ctx.restore()
    })
  }

  init () {
    document.addEventListener('contextmenu', this.eventContextmenu)
    this.resetCanvas()
    const name = prompt('你可以输入你的英雄名称!')
    this.initHeroName(name)
    // 创建boss
    this.createBoss()

    const executeAnimation = () => {
      this.resetCanvas()
      this.moveHero()
      this.drawBoss()
      this.config.animationID = requestAnimationFrame(executeAnimation)
    }
    this.config.animationID = requestAnimationFrame(executeAnimation)
  }
}

/**
 * 工厂函数，创建人物/角色/boss
 */
class RoleFactory {
  constructor({ width, height, imageSrc, run }) {
    this.roleConfig = {
      width,
      height,
      imageSrc,
      run
    }
    this.create()
  }
  create () {
    const image = new Image()
    image.src = this.roleConfig.imageSrc
    image.onload = this.roleConfig.run
    this.roleConfig = {
      ...this.roleConfig,
      updateTime: new Date().getTime(),
      image: image,
      index: 0,
      endFlag: false
    }
  }
}
